/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.test.crypto.symmetric

import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.Assertions.assertArrayEquals
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertTrue
import javax.crypto.Cipher
import java.security.SecureRandom

/**
 * AES 对称加密算法测试类
 *
 * 测试 AES 算法的具体实现，包括密钥验证、加密解密等功能
 *
 * @author tangli
 * @date 2025/01/22 20:00
 */
@DisplayName("AES 对称加密测试")
class AesTest {

    /**
     * 生成有效的 AES 密钥（32 字节）
     */
    private fun generateValidAesKey(): ByteArray {
        val key = ByteArray(32)
        SecureRandom().nextBytes(key)
        return key
    }

    /**
     * 生成固定的测试密钥
     */
    private fun generateFixedAesKey(): ByteArray {
        return "12345678901234567890123456789012".toByteArray(Charsets.UTF_8)
    }

    @Nested
    @DisplayName("AES 密钥验证测试")
    inner class AesKeyValidationTest {

        @Test
        @DisplayName("crypto(): 32字节密钥应该正常工作")
        fun testValidKeyLength() {
            // Given
            val validKey = generateValidAesKey()
            val testData = "AES 测试数据".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = tony.crypto.symmetric.Aes.crypto(testData, validKey, Cipher.ENCRYPT_MODE)
            val decrypted = tony.crypto.symmetric.Aes.crypto(encrypted, validKey, Cipher.DECRYPT_MODE)

            // Then
            assertNotNull(encrypted, "加密结果不应为空")
            assertTrue(encrypted.isNotEmpty(), "加密结果不应为空数组")
            assertArrayEquals(testData, decrypted, "32字节密钥加密解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 16字节密钥应该抛出异常")
        fun testInvalidKeyLength16Bytes() {
            // Given
            val invalidKey = ByteArray(16)
            val testData = "test".toByteArray()

            // When & Then
            val exception = assertThrows<IllegalArgumentException> {
                tony.crypto.symmetric.Aes.crypto(testData, invalidKey, Cipher.ENCRYPT_MODE)
            }
            assertEquals("IllegalAesKey, aesKey's length must be 32", exception.message)
        }

        @Test
        @DisplayName("crypto(): 24字节密钥应该抛出异常")
        fun testInvalidKeyLength24Bytes() {
            // Given
            val invalidKey = ByteArray(24)
            val testData = "test".toByteArray()

            // When & Then
            val exception = assertThrows<IllegalArgumentException> {
                tony.crypto.symmetric.Aes.crypto(testData, invalidKey, Cipher.ENCRYPT_MODE)
            }
            assertEquals("IllegalAesKey, aesKey's length must be 32", exception.message)
        }

        @Test
        @DisplayName("crypto(): 空密钥应该抛出异常")
        fun testEmptyKey() {
            // Given
            val emptyKey = ByteArray(0)
            val testData = "test".toByteArray()

            // When & Then
            val exception = assertThrows<IllegalArgumentException> {
                tony.crypto.symmetric.Aes.crypto(testData, emptyKey, Cipher.ENCRYPT_MODE)
            }
            assertEquals("IllegalAesKey, aesKey's length must be 32", exception.message)
        }

        @Test
        @DisplayName("crypto(): 超长密钥应该抛出异常")
        fun testOversizedKey() {
            // Given
            val oversizedKey = ByteArray(64)
            val testData = "test".toByteArray()

            // When & Then
            val exception = assertThrows<IllegalArgumentException> {
                tony.crypto.symmetric.Aes.crypto(testData, oversizedKey, Cipher.ENCRYPT_MODE)
            }
            assertEquals("IllegalAesKey, aesKey's length must be 32", exception.message)
        }
    }

    @Nested
    @DisplayName("AES 加密解密测试")
    inner class AesEncryptDecryptTest {

        private val validKey = generateFixedAesKey()

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理英文文本")
        fun testEncryptDecryptEnglishText() {
            // Given
            val englishText = "Hello, World! This is a test message.".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = tony.crypto.symmetric.Aes.encrypt(englishText, validKey)
            val decrypted = tony.crypto.symmetric.Aes.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(englishText, decrypted, "英文文本加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理中文文本")
        fun testEncryptDecryptChineseText() {
            // Given
            val chineseText = "你好，世界！这是一个测试消息。包含特殊字符：@#￥%……&*".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = tony.crypto.symmetric.Aes.encrypt(chineseText, validKey)
            val decrypted = tony.crypto.symmetric.Aes.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(chineseText, decrypted, "中文文本加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理二进制数据")
        fun testEncryptDecryptBinaryData() {
            // Given
            val binaryData = byteArrayOf(
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
                0xFF.toByte(), 0xFE.toByte(), 0xFD.toByte(),
                0x7F, 0x80.toByte(), 0x81.toByte()
            )

            // When
            val encrypted = tony.crypto.symmetric.Aes.encrypt(binaryData, validKey)
            val decrypted = tony.crypto.symmetric.Aes.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(binaryData, decrypted, "二进制数据加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理空数据")
        fun testEncryptDecryptEmptyData() {
            // Given
            val emptyData = ByteArray(0)

            // When
            val encrypted = tony.crypto.symmetric.Aes.encrypt(emptyData, validKey)
            val decrypted = tony.crypto.symmetric.Aes.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(emptyData, decrypted, "空数据加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt() 和 decrypt(): 应该正确处理大数据块")
        fun testEncryptDecryptLargeData() {
            // Given
            val largeData = "A".repeat(100000).toByteArray(Charsets.UTF_8)

            // When
            val encrypted = tony.crypto.symmetric.Aes.encrypt(largeData, validKey)
            val decrypted = tony.crypto.symmetric.Aes.decrypt(encrypted, validKey)

            // Then
            assertArrayEquals(largeData, decrypted, "大数据块加密解密应该正确")
        }

        @Test
        @DisplayName("encrypt(): 相同输入应该产生相同密文（ECB模式）")
        fun testEncryptDeterministic() {
            // Given
            val testData = "确定性测试数据".toByteArray(Charsets.UTF_8)

            // When
            val encrypted1 = tony.crypto.symmetric.Aes.encrypt(testData, validKey)
            val encrypted2 = tony.crypto.symmetric.Aes.encrypt(testData, validKey)

            // Then
            assertArrayEquals(encrypted1, encrypted2, "ECB模式下相同输入应该产生相同密文")
        }
    }

    @Nested
    @DisplayName("AES crypto() 方法测试")
    inner class AesCryptoMethodTest {

        private val validKey = generateFixedAesKey()

        @Test
        @DisplayName("crypto(): 加密模式应该正确工作")
        fun testCryptoEncryptMode() {
            // Given
            val testData = "crypto 方法加密测试".toByteArray(Charsets.UTF_8)

            // When
            val encrypted = tony.crypto.symmetric.Aes.crypto(testData, validKey, Cipher.ENCRYPT_MODE)
            val decrypted = tony.crypto.symmetric.Aes.crypto(encrypted, validKey, Cipher.DECRYPT_MODE)

            // Then
            assertNotNull(encrypted, "加密结果不应为空")
            assertTrue(encrypted.isNotEmpty(), "加密结果不应为空数组")
            assertArrayEquals(testData, decrypted, "crypto 方法加密解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 解密模式应该正确工作")
        fun testCryptoDecryptMode() {
            // Given
            val testData = "crypto 方法解密测试".toByteArray(Charsets.UTF_8)
            val encrypted = tony.crypto.symmetric.Aes.encrypt(testData, validKey)

            // When
            val decrypted = tony.crypto.symmetric.Aes.crypto(encrypted, validKey, Cipher.DECRYPT_MODE)

            // Then
            assertArrayEquals(testData, decrypted, "crypto 方法解密应该正确")
        }

        @Test
        @DisplayName("crypto(): 无效模式应该抛出异常")
        fun testCryptoInvalidMode() {
            // Given
            val testData = "test".toByteArray()
            val invalidMode = 999

            // When & Then
            assertThrows<Exception> {
                tony.crypto.symmetric.Aes.crypto(testData, validKey, invalidMode)
            }
        }
    }

    @Nested
    @DisplayName("AES 错误处理测试")
    inner class AesErrorHandlingTest {

        private val validKey = generateValidAesKey()

        @Test
        @DisplayName("decrypt(): 无效密文应该抛出异常")
        fun testDecryptInvalidCiphertext() {
            // Given
            val invalidCiphertext = "这不是有效的密文".toByteArray()

            // When & Then
            assertThrows<Exception> {
                tony.crypto.symmetric.Aes.decrypt(invalidCiphertext, validKey)
            }
        }

        @Test
        @DisplayName("decrypt(): 截断的密文应该抛出异常")
        fun testDecryptTruncatedCiphertext() {
            // Given
            val testData = "test data".toByteArray()
            val encrypted = tony.crypto.symmetric.Aes.encrypt(testData, validKey)
            val truncatedCiphertext = encrypted.copyOf(encrypted.size / 2)

            // When & Then
            assertThrows<Exception> {
                tony.crypto.symmetric.Aes.decrypt(truncatedCiphertext, validKey)
            }
        }

        @Test
        @DisplayName("decrypt(): 错误的密钥应该抛出异常")
        fun testDecryptWithWrongKey() {
            // Given
            val testData = "test data".toByteArray()
            val correctKey = generateValidAesKey()
            val wrongKey = generateValidAesKey()
            val encrypted = tony.crypto.symmetric.Aes.encrypt(testData, correctKey)

            // When & Then
            assertThrows<Exception> {
                tony.crypto.symmetric.Aes.decrypt(encrypted, wrongKey)
            }
        }
    }

    @Nested
    @DisplayName("AES 性能和稳定性测试")
    inner class AesPerformanceTest {

        @Test
        @DisplayName("encrypt() 和 decrypt(): 多次操作应该保持一致性")
        fun testMultipleOperationsConsistency() {
            // Given
            val key = generateValidAesKey()
            val testData = "AES 一致性测试数据".toByteArray(Charsets.UTF_8)

            // When & Then
            repeat(20) {
                val encrypted = tony.crypto.symmetric.Aes.encrypt(testData, key)
                val decrypted = tony.crypto.symmetric.Aes.decrypt(encrypted, key)
                assertArrayEquals(testData, decrypted, "第 ${it + 1} 次 AES 加密解密应该一致")
            }
        }

        @Test
        @DisplayName("crypto(): 不同数据大小应该正确处理")
        fun testDifferentDataSizes() {
            // Given
            val key = generateValidAesKey()
            val sizes = listOf(1, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129)

            // When & Then
            sizes.forEach { size ->
                val testData = "A".repeat(size).toByteArray(Charsets.UTF_8)
                val encrypted = tony.crypto.symmetric.Aes.crypto(testData, key, Cipher.ENCRYPT_MODE)
                val decrypted = tony.crypto.symmetric.Aes.crypto(encrypted, key, Cipher.DECRYPT_MODE)
                assertArrayEquals(testData, decrypted, "大小为 $size 的数据应该正确处理")
            }
        }
    }
}
